﻿using System.Text;
using LuckyShell.Parsers;
using LuckyShell.ShellOrders;

namespace LuckyShell.ShellValues;

public class ShellOperation(ShellValue left, ShellTokenGeneric token, ShellValue right) : ShellValue
{
    public object GetValue(ref VariateManager manager)
    {
        if (Left == null! && Token == ShellTokenGeneric.NOT)
        {
            var notResult = Right.GetValue(ref manager);
            if (notResult is bool b) return !b;
            return notResult;
        }

        if (Left == null! && Token == ShellTokenGeneric.MINUS)
        {
            var minusResult = Right.GetValue(ref manager);
            if (minusResult is decimal b) return -b;
            return minusResult;
        }

        if (Left == null! || Right == null!) return new object();

        var leftResult = Left.GetValue(ref manager);
        var rightResult = Right.GetValue(ref manager);

        if (leftResult is ShellValue leftValue) leftResult = leftValue.GetValue(ref manager);
        if (rightResult is ShellValue rightValue) rightResult = rightValue.GetValue(ref manager);

        if (Token == ShellTokenGeneric.PLUS)
        {
            if (leftResult is decimal leftDecimal && rightResult is decimal rightDecimal)
                return leftDecimal + rightDecimal;
            if (leftResult is string leftString && rightResult is decimal rightString)
                return leftString + rightString;
            throw new Exception($"Operation is error: {Left} {Token} {Right}");
        }

        if (Token == ShellTokenGeneric.MINUS)
        {
            if (leftResult is decimal leftDecimal && rightResult is decimal rightDecimal)
                return leftDecimal - rightDecimal;
            throw new Exception($"Operation is error: {Left} {Token} {Right}");
        }

        if (Token == ShellTokenGeneric.TIMES)
        {
            if (leftResult is decimal leftDecimal && rightResult is decimal rightDecimal)
                return leftDecimal * rightDecimal;
            if (leftResult is decimal leftStringDecimal && rightResult is string rightString)
            {
                var builder = new StringBuilder();
                for (var i = (int)leftStringDecimal; i > 0; i--)
                    builder.Append(rightString);
                return builder.ToString();
            }

            if (leftResult is string leftString && rightResult is decimal rightStringDecimal)
            {
                var builder = new StringBuilder();
                for (var i = (int)rightStringDecimal; i > 0; i--)
                    builder.Append(leftString);
                return builder.ToString();
            }

            throw new Exception($"Operation is error: {Left} {Token} {Right}");
        }

        if (Token == ShellTokenGeneric.DIVIDE)
        {
            if (leftResult is decimal leftDecimal && rightResult is decimal rightDecimal)
                return leftDecimal / rightDecimal;
            throw new Exception($"Operation is error: {Left} {Token} {Right}");
        }

        if (Token == ShellTokenGeneric.GREATER)
        {
            if (leftResult is decimal leftDecimal && rightResult is decimal rightDecimal)
                return leftDecimal > rightDecimal;
            throw new Exception($"Operation is error: {Left} {Token} {Right}");
        }

        if (Token == ShellTokenGeneric.LESSER)
        {
            if (leftResult is decimal leftDecimal && rightResult is decimal rightDecimal)
                return leftDecimal < rightDecimal;
            throw new Exception($"Operation is error: {Left} {Token} {Right}");
        }

        if (Token == ShellTokenGeneric.EQUALS)
            return leftResult.Equals(rightResult);

        if (Token == ShellTokenGeneric.DIFFERENT)
            return !leftResult.Equals(rightResult);

        if (Token == ShellTokenGeneric.OR)
        {
            if (leftResult is bool leftBool && rightResult is bool rightBool)
                return leftBool || rightBool;
            throw new Exception($"Operation is error: {Left} {Token} {Right}");
        }

        if (Token == ShellTokenGeneric.AND)
        {
            if (leftResult is bool leftBool && rightResult is bool rightBool)
                return leftBool && rightBool;
            throw new Exception($"Operation is error: {Left} {Token} {Right}");
        }


        throw new Exception($"Operation is error: {Left} {Token} {Right}");
    }

    private ShellValue Left { get; set; } = left;
    private ShellValue Right { get; set; } = right;
    private ShellTokenGeneric Token { get; set; } = token;

    public override string ToString() => $"{Left} {Token} {Right}";
}